package com.ming.liteflow.controller;

import cn.hutool.core.collection.CollUtil;
import com.ming.common.SelectOptionItem;
import com.ming.common.beetl.util.Result;
import com.ming.common.beetl.util.StrUtil;
import com.ming.common.util.ClassPathScanUtil;
import com.ming.common.xxljob.annotation.PermissionLimit;
import com.ming.common.liteflow.core.dynamic.IvyDynamicClass;
import com.ming.common.liteflow.core.el.NodeInfoToELUtil;
import com.ming.common.liteflow.core.exec.ClassExecUtil;
import com.ming.common.liteflow.core.exec.ELExecUtil;
import com.ming.common.liteflow.core.node.*;
import com.ming.common.liteflow.vo.IvyCmpVo;
import com.ming.common.Options;
import com.ming.common.SortBy;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.*;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.page.PageResult;
import org.beetl.sql.core.query.LambdaQuery;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/liteflow/cmp")
public class LiteFlowCmpController {

    @Resource
    private SQLManager sqlManager;

    @PostMapping("/page")
    @PermissionLimit(limit = false)
    public Result<PageResult<IvyCmp>> page(@RequestBody IvyCmpVo vo){
        LambdaQuery<IvyCmp> lambdaQuery = sqlManager.lambdaQuery(IvyCmp.class);
        lambdaQuery.andEq(IvyCmp::getType, LambdaQuery.filterEmpty(vo.getType()));
        lambdaQuery.andLike(IvyCmp::getComponentId, LambdaQuery.filterLikeEmpty(vo.getComponentId()));
        lambdaQuery.andLike(IvyCmp::getComponentName, LambdaQuery.filterLikeEmpty(vo.getComponentName()));
        Options options = vo.getOptions();
        List<SortBy> sortBy = options.getSortBy();
        for (SortBy sort : sortBy) {
            if ("desc".equalsIgnoreCase(sort.getOrder())) {
                lambdaQuery.desc(StrUtil.camelToSnake(sort.getKey()));
            } else {
                lambdaQuery.asc(StrUtil.camelToSnake(sort.getKey()));
            }
        }
        PageResult<IvyCmp> page = lambdaQuery.page(options.getPage(), options.getItemsPerPage());
        return Result.OK(page);
    }

    @PostMapping("/list")
    @PermissionLimit(limit = false)
    public Result<?> list(@RequestBody IvyCmpVo vo){
        String type = vo.getType();
        LambdaQuery<IvyCmp> query = sqlManager.lambdaQuery(IvyCmp.class);
        switch (type){
            case "NodeScriptComponent":
                query.andIn(IvyCmp::getType, CollUtil.toList(
                        NodeTypeEnum.SCRIPT.getCode(),
                        NodeTypeEnum.SWITCH_SCRIPT.getCode(),
                        NodeTypeEnum.IF_SCRIPT.getCode(),
                        NodeTypeEnum.FOR_SCRIPT.getCode(),
                        NodeTypeEnum.WHILE_SCRIPT.getCode(),
                        NodeTypeEnum.BREAK_SCRIPT.getCode()
                ));
                break;
            case "NodePreComponent":
            case "NodeFinallyComponent":
                query.andNotIn(IvyCmp::getType, CollUtil.toList(NodeTypeEnum.BREAK.getCode(),NodeTypeEnum.BREAK_SCRIPT.getCode(),NodeTypeEnum.FALLBACK.getCode()));
                break;
            default: query.andEq(IvyCmp::getType, LambdaQuery.filterEmpty(vo.getType()));break;
        }
        query.andLike(IvyCmp::getComponentId, LambdaQuery.filterLikeEmpty(vo.getComponentId()));
        query.andLike(IvyCmp::getComponentName, LambdaQuery.filterLikeEmpty(vo.getComponentName()));
        List<IvyCmp> list = query.select();
        if(NodeTypeEnum.FALLBACK.getCode().equalsIgnoreCase(type)){
            Set<Long> idSet = list.stream().filter(m->m.getFallbackId() != null).map(IvyCmp::getFallbackId).collect(Collectors.toSet());
            if(idSet.isEmpty()){
                Set<String> classSet = list.stream().map(IvyCmp::getClazz).collect(Collectors.toSet());
                Map<String,String> classMap = new HashMap<>();
                for (String className : classSet) {
                    try {
                        Class<?> clazz = Class.forName(className);
                        Class<?> superClass = clazz.getSuperclass();
                        if (superClass != null) {
                            String name = superClass.getSimpleName();
                            switch (name){
                                case "NodeComponent": classMap.put(className,NodeTypeEnum.COMMON.getCode());break;
                                case "NodeBreakComponent": classMap.put(className,NodeTypeEnum.BREAK.getCode());break;
                                case "NodeForComponent": classMap.put(className,NodeTypeEnum.FOR.getCode());break;
                                case "NodeIfComponent": classMap.put(className,NodeTypeEnum.IF.getCode());break;
                                case "NodeIteratorComponent": classMap.put(className,NodeTypeEnum.ITERATOR.getCode());break;
                                case "NodeSwitchComponent": classMap.put(className,NodeTypeEnum.SWITCH.getCode());break;
                                case "NodeWhileComponent": classMap.put(className,NodeTypeEnum.WHILE.getCode());break;
                                default: break;
                            }
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                list = list.stream().peek(m->m.setFallbackType(classMap.get(m.getClazz()))).collect(Collectors.toList());
            }else{
                LambdaQuery<IvyCmp> lambdaQuery = sqlManager.lambdaQuery(IvyCmp.class);
                lambdaQuery.andIn(IvyCmp::getId,idSet);
                List<IvyCmp> itemList = lambdaQuery.select();
                Map<Long, String> itemMap = itemList.stream().collect(Collectors.toMap(IvyCmp::getId, IvyCmp::getType));
                list = list.stream().peek(m->m.setFallbackType(itemMap.get(m.getFallbackId()))).collect(Collectors.toList());
            }
            return Result.OK(list.stream().collect(Collectors.groupingBy(IvyCmp::getFallbackType)));
        }
        return Result.OK(list);
    }

    @PostMapping("/fallback/list")
    @PermissionLimit(limit = false)
    public Result<?> fallbackList(@RequestBody IvyCmpVo vo){
        String type = vo.getType();
        LambdaQuery<IvyCmp> query = sqlManager.lambdaQuery(IvyCmp.class);
        switch (type){
            case "fallback":
                List<String> inList = CollUtil.toList(
                    NodeTypeEnum.SCRIPT.getCode(),
                    NodeTypeEnum.SWITCH_SCRIPT.getCode(),
                    NodeTypeEnum.IF_SCRIPT.getCode(),
                    NodeTypeEnum.FOR_SCRIPT.getCode(),
                    NodeTypeEnum.WHILE_SCRIPT.getCode(),
                    NodeTypeEnum.BREAK_SCRIPT.getCode(),
                    NodeTypeEnum.FALLBACK.getCode()
                );
                query.andNotIn(IvyCmp::getType, inList);
                break;
            default: query.andEq(IvyCmp::getType, LambdaQuery.filterEmpty(vo.getType()));break;
        }
        query.andLike(IvyCmp::getComponentId, LambdaQuery.filterLikeEmpty(vo.getComponentId()));
        query.andLike(IvyCmp::getComponentName, LambdaQuery.filterLikeEmpty(vo.getComponentName()));
        List<IvyCmp> list = query.select();
        return Result.OK(list);
    }

    @PostMapping("/add")
    @PermissionLimit(limit = false)
    public Result<Object> add(@RequestBody IvyCmp nodeInfo){
        LambdaQuery<IvyCmp> lambdaQuery = sqlManager.lambdaQuery(IvyCmp.class);
        lambdaQuery.andEq(IvyCmp::getComponentId, nodeInfo.getComponentId());
        long count = lambdaQuery.count();
        if(count > 0){
            return Result.error("组件ID重复");
        }
        nodeInfo.setEl(NodeInfoToELUtil.toEL(nodeInfo,false));
        nodeInfo.setElFormat(NodeInfoToELUtil.toEL(nodeInfo,true));
        nodeInfo.setId(null);
        int i = sqlManager.insert(IvyCmp.class, nodeInfo);
        return Result.OK("新增成功",i);
    }

    @PostMapping("/update")
    @PermissionLimit(limit = false)
    public Result<Object> update(@RequestBody IvyCmp nodeInfo){
        LambdaQuery<IvyCmp> lambdaQuery = sqlManager.lambdaQuery(IvyCmp.class);
        lambdaQuery.andNotEq(IvyCmp::getId, nodeInfo.getId());
        lambdaQuery.andEq(IvyCmp::getComponentId, nodeInfo.getComponentId());
        long count = lambdaQuery.count();
        if(count > 0){
            return Result.error("组件ID重复");
        }
        nodeInfo.setEl(NodeInfoToELUtil.toEL(nodeInfo,false));
        nodeInfo.setElFormat(NodeInfoToELUtil.toEL(nodeInfo,true));
        int i = sqlManager.updateById(nodeInfo);
        return Result.OK("更新成功", i);
    }

    @PostMapping("/delete")
    @PermissionLimit(limit = false)
    public Result<Integer> delete(@RequestBody IvyCmp nodeInfo){
        LambdaQuery<IvyCmp> lambdaQuery = sqlManager.lambdaQuery(IvyCmp.class);
        lambdaQuery.andEq(IvyCmp::getComponentId, nodeInfo.getComponentId());
        int i = lambdaQuery.delete();
        return Result.OK("删除成功",i);
    }

    @PostMapping("/exec")
    @PermissionLimit(limit = false)
    public Result<Object> exec(@RequestBody IvyCmp nodeInfo){
        LambdaQuery<IvyCmp> lambdaQuery = sqlManager.lambdaQuery(IvyCmp.class);
        lambdaQuery.andEq(IvyCmp::getId, nodeInfo.getId());
        IvyCmp info = lambdaQuery.single();
        try {
            if(cn.hutool.core.util.StrUtil.isNotBlank(info.getClazz())){
                IvyDynamicClass dynamicClass = sqlManager.lambdaQuery(IvyDynamicClass.class).andEq(IvyDynamicClass::getClassId, info.getClazz()).single();
                if(dynamicClass != null){
                    ClassExecUtil.buildCmp(dynamicClass);
                }
            }else{
                CmpScriptUtil.createNode(info);
            }
            String el = info.getEl();
            System.out.println("EL："+el);
            String execResult = ELExecUtil.exec(info);
            return Result.OK("执行成功",execResult);
        }catch (Exception e){
            e.printStackTrace();
            return Result.error(e.getMessage());
        }
    }

    @PostMapping("/options")
    @PermissionLimit(limit = false)
    public Result<?> options(@RequestBody Map<String,Object> map){
        String type = (String) map.get("type");
        if(cn.hutool.core.util.StrUtil.isBlank(type)){
            return Result.OK(new ArrayList<>());
        }
        Set<Class<?>> classesSet = null;
        String basePackage = "com.ming";
        switch (type){
            case "common":
                classesSet = ClassPathScanUtil.scanBySuper(NodeComponent.class, basePackage);
                break;
            case "switch":
                classesSet = ClassPathScanUtil.scanBySuper(NodeSwitchComponent.class, basePackage);
                break;
            case "if":
                classesSet = ClassPathScanUtil.scanBySuper(NodeIfComponent.class, basePackage);
                break;
            case "for":
                classesSet = ClassPathScanUtil.scanBySuper(NodeForComponent.class, basePackage);
                break;
            case "while":
                classesSet = ClassPathScanUtil.scanBySuper(NodeWhileComponent.class, basePackage);
                break;
            case "break":
                classesSet = ClassPathScanUtil.scanBySuper(NodeBreakComponent.class, basePackage);
                break;
            case "iterator":
                classesSet = ClassPathScanUtil.scanBySuper(NodeIteratorComponent.class, basePackage);
                break;
            default:
                classesSet = ClassPathScanUtil.scanByAnno(LiteflowComponent.class, basePackage);
                break;
        }
        List<SelectOptionItem> itemList0 = classesSet.stream().map(m -> new SelectOptionItem("【spring bean】"+m.getName(), m.getName())).collect(Collectors.toList());

        LambdaQuery<IvyDynamicClass> lambdaQuery = sqlManager.lambdaQuery(IvyDynamicClass.class);
        List<IvyDynamicClass> list = lambdaQuery.select(IvyDynamicClass::getId,IvyDynamicClass::getClassId,IvyDynamicClass::getClassName);
        List<SelectOptionItem> itemList1 = list.stream().map(m -> new SelectOptionItem("【动态类】" + m.getClassName() + m.getClassId(), m.getClassId())).collect(Collectors.toList());
        itemList0.addAll(itemList1);
        return Result.OK(itemList0);
    }

    @PostMapping("/cmpOptions")
    @PermissionLimit(limit = false)
    public Result<?> cmpOptions(@RequestBody Map<String,Object> map){
        LambdaQuery<IvyCmp> lambdaQuery = sqlManager.lambdaQuery(IvyCmp.class);
        List<IvyCmp> list = lambdaQuery.select(IvyCmp::getId,IvyCmp::getComponentId,IvyCmp::getComponentName);
        List<SelectOptionItem> itemList = list.stream().map(m -> new SelectOptionItem("【组件】" + m.getComponentName() + m.getComponentId(), m.getComponentId())).collect(Collectors.toList());
        return Result.OK(itemList);
    }
}
